package model.editormodel;

import java.util.List;
import java.util.Map;

/**
 * <b>In Verbindung stehende Klassen:</b> <code>LogicEditorElement, Output
 * </code><br>
 * <b>Funktion:</b> Umsetzung eines Eingangs in einer Komponente <br>
 * <br>
 * Diese Klasse dient zur Repraesentation eines Eingangs auf einem Bauteil. Die
 * <code>Input</code>-Klasse speichert dabei seine zugehoerige
 * <code>Output</code>-Klasse mit der es verbunden ist, welche sozusagen die
 * Quelle darstellt. Ausserdem existiert eine Referenz auf sein Wirtelement (
 * <code>LogicEditorElement</code> ), auf dem sich der <code>Input</code>
 * befindet.
 * 
 * @author Team Locsi
 * 
 */
public class Input {

	public final static String STD_A = "a";
	public final static String STD_B = "b";
	public final static String SELECT = "sel";

	public final static String FF_SET = "set";
	public final static String FF_RESET = "reset";

	private String name;
	private Output sourceOutput;
	private LogicEditorElement myElement;
	private double xRelToElement;
	private double yRelToElement;

	private LogicEditorElement mySDElement;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>Input</code>-Objekt.
	 * 
	 * @param myElement
	 *            <code>LogicEditorElement</code> - Referenz auf das zugehoerige
	 *            <code>LogicEditorElement</code>
	 * @throws <code>IllegalArgumetnException</code>, falls null uebergeben wird
	 */
	public Input(LogicEditorElement myElement) {
		if (myElement == null) {
			throw new IllegalArgumentException("myElement darf nicht null sein");
		}
		this.myElement = myElement;
		initializeCoordinates();
	}

	/**
	 * Diese Methode generiert an Hand des Typs von dem uebergebenen myElement,
	 * welchen Namen das Input-Objekt erhaelt. Dabei wird nur geprueft ob es
	 * sich dabei um das erste oder zweite Input Objekt handelt. Es prueft zum
	 * Beispiel nicht ob die Anzahl der Input-Objekte eines Elements bereits
	 * erschoepft ist.
	 */
	private void initializeCoordinates() {
		switch (myElement.getType()) {

		case AND:
			if (!myElement.inputs.containsKey(Input.STD_A)) {
				this.name = Input.STD_A;
			} else {
				this.name = Input.STD_B;
			}
			break;
		case NAND:
			if (!myElement.inputs.containsKey(Input.STD_A)) {
				this.name = Input.STD_A;
			} else {
				this.name = Input.STD_B;
			}
			break;
		case OR:
			if (!myElement.inputs.containsKey(Input.STD_A)) {
				this.name = Input.STD_A;
			} else {
				this.name = Input.STD_B;
			}
			break;
		case XOR:
			if (!myElement.inputs.containsKey(Input.STD_A)) {
				this.name = Input.STD_A;
			} else {
				this.name = Input.STD_B;
			}
			break;

		case NOT:
			this.name = Input.STD_A;
			break;

		case LAMP:
			this.name = Input.STD_A;
			break;

		case NODE:
			this.name = Input.STD_A;
			break;

		case SWITCH:
			this.name = Input.STD_A;
			break;

		case TWO_MX:
			if (!myElement.inputs.containsKey(Input.STD_A)) {
				this.name = Input.STD_A;
			} else if (!myElement.inputs.containsKey(Input.STD_B)) {
				this.name = Input.STD_B;
			} else {
				this.name = Input.SELECT;
			}
			break;

		case FLIPFLOP:
			if (!myElement.inputs.containsKey(Input.FF_SET)) {
				this.name = Input.FF_SET;
			} else {
				this.name = Input.FF_RESET;
			}
			break;

		default:
			break;
		}

		// mit der Methode getStandardInputCoordinate wird an Hand des gesetzten
		// Namens die zugehoerigen Koordinaten ermittelt
		int[] coords = myElement.getType()
				.getStandardInputCoordinate(this.name);
		xRelToElement = coords[0];
		yRelToElement = coords[1];
	}

	/**
	 * Setzt den zugehoerigen <code>Output</code>.
	 * 
	 * @param sourceOutput
	 *            <code>Output</code> - Referenz auf den zugehoerigen
	 *            <code>Output</code>
	 */
	protected void setSourceOutput(Output sourceOutput) {
		this.sourceOutput = sourceOutput;
	}

	/**
	 * Gibt den zugehoerigen <code>Output</code> zurueck.
	 * 
	 * @return <code>Output</code> - den zugehoerigen <code>Output</code>.
	 */
	public Output getSourceOutput() {
		return sourceOutput;
	}

	/**
	 * Gibt das zugehoerige <code>LogicEditorElement</code> zurueck. Entweder
	 * das LogicSelfDefined element auf dem es liegt, oder wenn es auf keinem
	 * liegt das normale.
	 * 
	 * @return <code>LogicEditorElement</code> - das zugehoerige
	 *         <code>LogicEditorElement</code>.
	 */
	public LogicEditorElement getMySuperElement() {
		if (mySDElement != null) {
			return mySDElement;
		} else {
			return myElement;
		}
	}

	/**
	 * Setzt das SuperElement des Inputs (selbstdefiniertes Bauteil)
	 * 
	 * @param mySDElement
	 */
	public void setMySuperElement(LogicEditorElement mySDElement) {
		this.mySDElement = mySDElement;
	}

	/**
	 * Methode die immer das Ursprungselement zur�ckgibt auf dem es liegt, auch
	 * wenn es jetzt auf einem selbstdefinierten Bauteil liegt
	 * 
	 * @return LogicEditorElement
	 */
	public LogicEditorElement getMyElement() {
		return myElement;
	}

	/**
	 * Gibt den Namen des <code>Input</code> zurueck.
	 * 
	 * @return <code>String</code> - den Namen des <code>Input</code>
	 */
	public String getName() {
		return name;
	}

	/**
	 * Gibt den Namen des Eingangs zurueck den er nach aussen hat. Der ist
	 * eventuell ncihtomehr der standardname weil er als ausgang eines
	 * selbstdefinierten bauteils dient. sonst den standardnamen
	 * 
	 * @return <code>String</code>  - Name des Eingangs
	 */
	public String getOuterName() {
		if (mySDElement != null) {
			LogicSelfdefinedEditorElement sdElement = (LogicSelfdefinedEditorElement) mySDElement;
			for (Map.Entry<String, List<Input>> ent : sdElement
					.getListOfInputs().entrySet()) {
				for (Input inp : ent.getValue()) {
					if (inp.equals(this)) {
						return ent.getKey();
					}
				}
			}
		}
		return name;
	}

	/**
	 * Setzt die x-Koordinate des <code>Input</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>.
	 * 
	 * @param xRelToElement
	 *            <code>double</code> - x-Koordinate des <code>Input</code> in
	 *            Pixel
	 */
	protected void setXRelToElement(double xRelToElement) {
		this.xRelToElement = xRelToElement;
	}

	/**
	 * Gibt die x-Koordinate des <code>Input</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>, zurueck.
	 * 
	 * @return <code>double</code> - die x-Koordinate des <code>Input</code> in
	 *         Pixel
	 */
	public double getXRelToElement() {
		return xRelToElement;
	}

	/**
	 * Setzt die y-Koordinate des <code>Input</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>.
	 * 
	 * @param yRelToElement
	 *            <code>double</code> - y-Koordinate des <code>Input</code> in
	 *            Pixel
	 */
	protected void setYRelToElement(double yRelToElement) {
		this.yRelToElement = yRelToElement;
	}

	/**
	 * Gibt die y-Koordinate des <code>Input</code> in Pixel, relativ zum
	 * zugehoerigen <code>LogicEditorElement</code>, zurueck.
	 * 
	 * @return <code>double</code> - die y-Koordinate des <code>Input</code> in
	 *         Pixel
	 */
	public double getYRelToElement() {
		return yRelToElement;
	}
}